﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.Lync.Model;
using System.IO;

namespace LyncContactStatusChangeAlerts
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }

        // http://msdn.microsoft.com/en-us/library/hh378607.aspx

        private LyncClient lyncClient;
        StreamWriter log = new StreamWriter(Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\lynclog.txt", true);
        private ContactManager _ContactManager = null;
        private Contact _Contact = null;
        private List<SearchProviders> myActiveSearchProviders = new List<SearchProviders>();
        private ContactSubscription _SearchResultSubscription = null;
        private SearchResults results = null;

        private void btnStart_Click(object sender, EventArgs e)
        {
            if (lyncClient.State == ClientState.SignedIn)
            {
                string searchName = tbContact.Text;
                object[] _asyncState = { lyncClient.ContactManager, searchName };
                lyncClient.ContactManager.BeginSearch(searchName, SearchResultsCallback, _asyncState);
            }
        }

        /// Handles callback containing results of a search
        /// </summary>c
        /// <param name="source"></param>
        /// <param name="results"></param>
        /// <param name="_asyncOperation"></param>
        public void SearchResultsCallback(IAsyncResult ar)
        {
            if (ar.IsCompleted == true)
            {
                object[] _asyncState = (object[])ar.AsyncState;
                try
                {
                    results = ((ContactManager)_asyncState[0]).EndSearch(ar);
                    if (results.AllResults.Count != 0)
                    {
                        //subscribe
                        SubscribeToSearchResults(results.Contacts);
                        _Contact = results.Contacts[0];
                        _Contact.ContactInformationChanged += new EventHandler<ContactInformationChangedEventArgs>(_Contact_ContactInformationChanged);
                    }
                    else
                    {
                        MessageBox.Show("0 instances found for " + _asyncState[1].ToString());
                    }
                }
                catch (SearchException se)
                {
                    MessageBox.Show("Search failed: " + se.Reason.ToString());
                }
            }
        }

        /// <summary>
        /// Adds contacts found through a search to a ContactSubscription and raises
        /// ContactAddedEvent to UI.
        /// </summary>
        /// <param name="pContact">List[Contact]. The list of contacts found in a search.</param>
        /// <param name="subscribeContext">string. The context in which this method is called.</param>
        public void SubscribeToSearchResults(IList<Contact> pContactList)
        {
            try
            {
                if (_SearchResultSubscription == null)
                {
                    _SearchResultSubscription = _ContactManager.CreateSubscription();
                }
                else
                {
                    //remove all existing search results
                    _SearchResultSubscription.Unsubscribe();
                    foreach (Contact c in _SearchResultSubscription.Contacts)
                    {
                        _SearchResultSubscription.RemoveContact(c);
                    }
                }

                //add the Contact to a ContactSubscription
                _SearchResultSubscription.AddContacts(pContactList);

                //Specify the Contact Information Types to be returned in ContactInformationChanged events.
                ContactInformationType[] ContactInformationTypes = { ContactInformationType.Availability, ContactInformationType.ActivityId };

                //Activate the subscription
                _SearchResultSubscription.Subscribe(ContactSubscriptionRefreshRate.High, ContactInformationTypes);
            }
            catch (Exception) { }
        }


        void _Contact_ContactInformationChanged(object sender, ContactInformationChangedEventArgs e)
        {
            try
            {
                string newCard = string.Empty;
                StringBuilder sb = new StringBuilder();
                if (e.ChangedContactInformation.Contains(ContactInformationType.Availability))
                {
                    object availability = ((Contact)sender).GetContactInformation(ContactInformationType.Availability);
                    object activityString = ((Contact)sender).GetContactInformation(ContactInformationType.Activity);
                    if (availability != null)
                    {
                        sb.Append(availability.ToString() + " " + activityString.ToString());
                        newCard = DateTime.Now.ToString() + " Updated availability for "
                        + ((Contact)sender).GetContactInformation(ContactInformationType.DisplayName).ToString()
                        + System.Environment.NewLine
                        + sb.ToString() + System.Environment.NewLine;

                        log.WriteLine(newCard);
                        log.Flush();

                        this.Invoke((MethodInvoker)delegate { lbLog.Items.Add(newCard); }); 
                    }
                }

            }
            catch (System.IO.InvalidDataException x)
            {
                MessageBox.Show("Invalid Data Exception, Contact.ContactInformationChanged " + x.Message);
            }
        }

        void ContactManager_SearchProviderStateChanged(Object source, SearchProviderStateChangedEventArgs data)
        {
            if (data.NewStatus == SearchProviderStatusType.SyncSucceeded)
            {
                myActiveSearchProviders.Add(data.Provider);
            }
        }


        private void MainForm_Load(object sender, EventArgs e)
        {
            //Listen for events of changes in the state of the client
            try
            {
                lyncClient = LyncClient.GetClient();
                this._ContactManager = lyncClient.ContactManager;
                this._SearchResultSubscription = _ContactManager.CreateSubscription();
                _ContactManager.SearchProviderStateChanged += ContactManager_SearchProviderStateChanged;

            }
            catch (ClientNotFoundException clientNotFoundException)
            {
                Console.WriteLine(clientNotFoundException);
                return;
            }
            catch (NotStartedByUserException notStartedByUserException)
            {
                Console.Out.WriteLine(notStartedByUserException);
                return;
            }
            catch (LyncClientException lyncClientException)
            {
                Console.Out.WriteLine(lyncClientException);
                return;
            }
            catch (SystemException systemException)
            {
                if (IsLyncException(systemException))
                {
                    // Log the exception thrown by the Lync Model API.
                    Console.WriteLine("Error: " + systemException);
                    return;
                }
                else
                {
                    // Rethrow the SystemException which did not come from the Lync Model API.
                    throw;
                }
            }
        }

        /// <summary>
        /// Identify if a particular SystemException is one of the exceptions which may be thrown
        /// by the Lync Model API.
        /// </summary>
        /// <param name="ex"></param>
        /// <returns></returns>
        private bool IsLyncException(SystemException ex)
        {
            return
                ex is NotImplementedException ||
                ex is ArgumentException ||
                ex is NullReferenceException ||
                ex is NotSupportedException ||
                ex is ArgumentOutOfRangeException ||
                ex is IndexOutOfRangeException ||
                ex is InvalidOperationException ||
                ex is TypeLoadException ||
                ex is TypeInitializationException ||
                ex is InvalidCastException;
        }
    }
}
